/*
 * Copyright (c) 2012, Marcin Czekaj
 * All rights reserved.
 *
 * This software is open-source under the BSD license; see LICENSE.TXT for a description.
 */
package pl.edu.uj.fais.gui;

import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;

import pl.edu.uj.fais.controller.Controller;
import pl.edu.uj.fais.graph.GraphStyle;
import pl.edu.uj.fais.graph.elements.Link;
import pl.edu.uj.fais.graph.elements.Node;
import pl.edu.uj.fais.graph.elements.ProxyNode;
import pl.edu.uj.fais.mode.GraphMode;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.picking.PickedState;

public class GraphCanvas extends VisualizationViewer<Node, Link> {
    
    private static final long serialVersionUID = -6315234547857839749L;
    
    private Controller controller = Controller.getInstance();
    
    private boolean edgeSelectionMode;
    private PickedState<Node> selectedNodes;
    private PickedState<Link> selectedLinks;
    
    public GraphCanvas(Layout<Node, Link> layout) {
        
        super(layout);
        
        controller.centerProxyNodes();
        
        initSelectedElements();
        initEdgeSelection();
        initStyle();
        
        addEventListeners();
    }
    
    public void setEdgeSelection(boolean enabled) {
        edgeSelectionMode = enabled;
    }
    
    private void initSelectedElements() {
        selectedNodes = getPickedVertexState();
        selectedLinks = getPickedEdgeState();
    }
    
    private void initEdgeSelection() {
        
        GraphMode mode = controller.getGraphMode();
        
        if (mode == GraphMode.HYPERGRAPH) {
            edgeSelectionMode = false;
        } else {
            edgeSelectionMode = true;
        }
    }
    
    private void initStyle() {
        GraphStyle.apply(this);
    }
    
    private void addEventListeners() {
        
        addGraphMouseListener(new GraphMouseListener<Node>() {
            
            private Point2D.Double lastPosition;
            
            @Override
            public void graphReleased(Node node, MouseEvent event) {
                
                if (lastPosition != null) {
                    
                    Point2D.Double current = getPositionOf(node);
                    
                    if (!current.equals(lastPosition)) {
                        controller.setChanged();
                    }
                }
            }
            
            @Override
            public void graphPressed(Node node, MouseEvent event) {
                lastPosition = getPositionOf(node);
            }
            
            @Override
            public void graphClicked(Node node, MouseEvent event) {
            }
            
            private Point2D.Double getPositionOf(Node node) {
                double x = controller.getLayout().getX(node);
                double y = controller.getLayout().getY(node);
                return new Point2D.Double(x, y);
            }
        });
        
        selectedNodes.addItemListener(new ItemListener() {
            
            @Override
            public void itemStateChanged(ItemEvent event) {
                
                Node node = (Node) event.getItem();
                
                if (edgeSelectionMode) {
                    selectedNodes.pick(node, false);
                } else {
                    
                    GraphMode mode = controller.getGraphMode();
                    
                    if (mode == GraphMode.HYPERGRAPH) {
                        switch (event.getStateChange()) {
                            case ItemEvent.SELECTED:
                                
                                if (node.isProxyNode()) {
                                    controller.selectHyperEdge(node);
                                }
                                
                                break;
                            case ItemEvent.DESELECTED:
                                
                                if (node.isProxyNode()) {
                                    controller.unselectHyperEdge(node);
                                }
                                
                                break;
                        }
                    }
                }
            }
        });
        
        selectedLinks.addItemListener(new ItemListener() {
            
            @Override
            public void itemStateChanged(ItemEvent event) {
                
                GraphMode mode = controller.getGraphMode();
                
                if (mode == GraphMode.HYPERGRAPH) {
                    
                    Link link = (Link) event.getItem();
                    ProxyNode node = link.getProxyNode();
                    
                    switch (event.getStateChange()) {
                        case ItemEvent.SELECTED:
                            if (!edgeSelectionMode) {
                                controller.selectHyperEdge(node);
                            }
                            break;
                        case ItemEvent.DESELECTED:
                            controller.unselectHyperEdge(node);
                            break;
                    }
                }
            }
        });
    }
}
